/* This file is part of REWise.
 *
 * REWise is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * REWise is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
 */

/*
DISCLAIMER
----------
The way the data is interpreted may be very wrong! It is the result of many
hours of puzzling with ImHex and this is what made the most sense to me with
the data (different installer executables) available at this time. Also what
most values represent inside a operation struct is still a mystery.

Another source of getting insight on how the Wise installer handles this would
be to disassemble wise0132.dll, I have done this with Cutter and its Ghidra
plugin but it was very hard since I couldn't rename about half of the variables,
which is a known issue of Cutter + the Ghidra plugin (at this time), but it
gave some hints.


INSEPCTED INSTALLERS
--------------------

NAME          MD5                               FILE
----          ---                               ----
HL:CS  (CD)   43cd9aff74e04fa1b59321b898d9a6bd  counter-strike.exe
HLGOTY (CD)   f5b8b35ca02beeeb146e62a63a0273a6  SETUP.EXE
CS15          eedcfcf6545cef92f26fb9a7fdd77c42  csv15full.exe
RTCW   (CD)   f2d9e3e1eaaed66047210881d130384f  Setup.exe
ET            5cc104767ecdf0feb3a36210adf46a8e  WolfET.exe

The way this program currently interprets the binary Wise script file is as
follows:

 1. Read 'struct WiseScriptHeader'
 2. Read 'struct WiseScriptTexts'
 3. Read one byte as operation code, we use this value to determine what struct
    to read, after that struct is read, continue this step (read one operation
    byte again etc..)

Operation codes:

  0x00  // Custom deflate file header
  0x03  // ?
  0x04  // Form data?
  0x05  // .ini file, section-name and values for that section
  0x06  // Deflated file just used by the installer? (No filename)
  0x07
  0x08
  0x09
  0x0A
  0x0B
  0x0C
  0x0D  // Skip this byte?
  0x0F  // Start form data?
  0x10  // End form data?
  0x11
  0x12  // File on install medium (CD/DVD), to copy?
  0x14  // Deflated file just used by the installer? (No filename)
  0x15
  0x16  // Temp filename?
  0x17
  0x18  // Skip this byte? ET suggests to skip all tailing zeros
  0x23
  0x24  // Skip this byte? Only seen in RTCW
  0x25  // Skip this byte? Only seen in RTCW
  0x1B  // Skip this byte?
  0x1C
  0x1E
*/

#ifndef H_WISESCRIPT
#define H_WISESCRIPT

#include <stdio.h>
#include <stdint.h>


#include "errors.h"

#define WIN_PATH_MAX 260


/* WiseScriptHeader */
typedef struct {
  unsigned char unknown_44[44];
  char * logPath; // \0 terminated string
  char * font;    // \0 terminated string
  unsigned char unknown_14[14];
} WiseScriptHeader;

/* WiseScriptTexts */
typedef struct {
  // 56 \0 terminated strings
  char * installTexts[56];
} WiseScriptTexts;

/* 0x00 WiseScriptFileHeader */
typedef struct {
  unsigned char unknown_2[2];   // seen: 0x8000, 0x8100, 0x0000, 0x9800 0xA100
  uint32_t deflateStart;
  uint32_t deflateEnd;
  uint16_t date;
  uint16_t time;
  uint32_t inflatedSize;
  unsigned char unknown_40[20];
  uint32_t crc32;
  char * destFile;     // \0 terminated string
  char * fileText;     // \0 terminated string
  unsigned char terminator;     // always \0? terminator?
} WiseScriptFileHeader;

/* WiseScriptUnknown0x03 */
typedef struct {
  unsigned char unknown_1;         // unknown
  char * unknownString_1; // \0 terminated string
  char * unknownString_2; // \0 terminated string
} WiseScriptUnknown0x03;

/* WiseScriptUnknown0x04 Form data? */
typedef struct {
  unsigned char no;           // read this struct again until 'no' == 0 ?
  char * dataString; // \0 terminated string
} WiseScriptUnknown0x04;

/* WiseScriptUnknown0x05 Something with .ini file */
typedef struct {
  // write .ini file?
  char * file;    // open for writing in append mode
  char * section; // ini section text
  char * values;  // multiline string containing values.
} WiseScriptUnknown0x05;

/* WiseScriptUnknown0x06 deflated Wise file? */
typedef struct {
  unsigned char unknown[6];
  uint32_t deflateStart;
  uint32_t deflateEnd;
  uint32_t inflatedSize;
  unsigned char unknown1; // terminator?
} WiseScriptUnknown0x06;

/* WiseScriptUnknown0x07 */
typedef struct {
  unsigned char unknown_1;
  char * unknownString_1;
  char * unknownString_2;
  char * unknownString_3;
} WiseScriptUnknown0x07;

/* WiseScriptUnknown0x08 */
typedef struct {
  unsigned char unknown_1;
} WiseScriptUnknown0x08;

/* WiseScriptUnknown0x09 */
typedef struct {
  unsigned char unknown_1;
  unsigned char unknown_2; // only when NOT 0x0901 or 0x0920
  char * unknownString_1;
  char * unknownString_2;
  char * unknownString_3;
  char * unknownString_4;
  char * unknownString_5; // only when 0x0901 or 0x0920
} WiseScriptUnknown0x09;

/* WiseScriptUnknown0x0A */
typedef struct {
  unsigned char unknown_2[2]; // 0x0200
  char * unknownString_1;
  char * unknownString_2;
  char * unknownString_3;
} WiseScriptUnknown0x0A;

/* WiseScriptUnknown0x0B */
typedef struct {
  unsigned char unknown_1;
  char * unknownString_1;
} WiseScriptUnknown0x0B;

/* WiseScriptUnknown0x0C */
typedef struct {
  unsigned char unknown_1;
  char * unknownString_1;
  char * unknownString_2;
} WiseScriptUnknown0x0C;

/* WiseScriptUnknown0x11 */
typedef struct {
  char * unknownString_1;
} WiseScriptUnknown0x11;

/* WiseScriptUnknown0x12 File on install medium (CD/DVD) */
typedef struct {
  unsigned char unknown_1; // 0C
  unsigned char unknown_41[41];
  char * sourceFile;
  char * unknownString_1;
  char * unknownString_2;
  char * destFile;
} WiseScriptUnknown0x12;

/* WiseScriptUnknown0x14 Wise script file? */
typedef struct {
  uint32_t deflateStart;
  uint32_t deflateEnd;
  uint32_t inflatedSize;
  char * name;
  char * message;
} WiseScriptUnknown0x14;

/* WiseScriptUnknown0x15 */
typedef struct {
  unsigned char unknown_1;
  char * unknownString_1;
  char * unknownString_2;
} WiseScriptUnknown0x15;

/* WiseScriptUnknown0x16 (TempFileName) */
typedef struct {
  char * name;
} WiseScriptUnknown0x16;

/* WiseScriptUnknown0x17 */
typedef struct {
  unsigned char unknown_1;
  unsigned char unknown_4[4];
  char * unknownString_1;
} WiseScriptUnknown0x17;

/* WiseScriptUnknown0x1C */
typedef struct {
  char * unknownString_1;
} WiseScriptUnknown0x1C;

/* WiseScriptUnknown0x1E */
typedef struct {
  unsigned char unknown_2[2];
} WiseScriptUnknown0x1E;

/* WiseScriptUnknown0x23 */
typedef struct {
  unsigned char unknown_1;
  char * unknownString_1;
  char * unknownString_2;
} WiseScriptUnknown0x23;


REWError readWiseScriptHeader(FILE * fp, WiseScriptHeader * header);
REWError readWiseScriptTexts(FILE * fp, WiseScriptTexts * texts);
REWError readWiseScriptFileHeader(FILE * fp, WiseScriptFileHeader * data);
REWError readWiseScriptUnknown0x03(FILE * fp, WiseScriptUnknown0x03 * data);
REWError readWiseScriptUnknown0x04(FILE * fp, WiseScriptUnknown0x04 * data);
REWError readWiseScriptUnknown0x05(FILE * fp, WiseScriptUnknown0x05 * data);
REWError readWiseScriptUnknown0x06(FILE * fp, WiseScriptUnknown0x06 * data); // no-free (no strings)
REWError readWiseScriptUnknown0x07(FILE * fp, WiseScriptUnknown0x07 * data);
REWError readWiseScriptUnknown0x08(FILE * fp, WiseScriptUnknown0x08 * data); // no-free (no strings)
REWError readWiseScriptUnknown0x09(FILE * fp, WiseScriptUnknown0x09 * data);
REWError readWiseScriptUnknown0x0A(FILE * fp, WiseScriptUnknown0x0A * data);
REWError readWiseScriptUnknown0x0B(FILE * fp, WiseScriptUnknown0x0B * data);
REWError readWiseScriptUnknown0x0C(FILE * fp, WiseScriptUnknown0x0C * data);
REWError readWiseScriptUnknown0x11(FILE * fp, WiseScriptUnknown0x11 * data);
REWError readWiseScriptUnknown0x12(FILE * fp, WiseScriptUnknown0x12 * data);
REWError readWiseScriptUnknown0x14(FILE * fp, WiseScriptUnknown0x14 * data);
REWError readWiseScriptUnknown0x15(FILE * fp, WiseScriptUnknown0x15 * data);
REWError readWiseScriptUnknown0x16(FILE * fp, WiseScriptUnknown0x16 * data);
REWError readWiseScriptUnknown0x17(FILE * fp, WiseScriptUnknown0x17 * data);
REWError readWiseScriptUnknown0x1C(FILE * fp, WiseScriptUnknown0x1C * data);
REWError readWiseScriptUnknown0x1E(FILE * fp, WiseScriptUnknown0x1E * data); // no-free (no strings)
REWError readWiseScriptUnknown0x23(FILE * fp, WiseScriptUnknown0x23 * data);

void freeWiseScriptHeader(WiseScriptHeader * header);
void freeWiseScriptTexts(WiseScriptTexts * texts);
void freeWiseScriptFileHeader(WiseScriptFileHeader * data);
void freeWiseScriptUnknown0x03(WiseScriptUnknown0x03 * data);
void freeWiseScriptUnknown0x04(WiseScriptUnknown0x04 * data);
void freeWiseScriptUnknown0x05(WiseScriptUnknown0x05 * data);
void freeWiseScriptUnknown0x07(WiseScriptUnknown0x07 * data);
void freeWiseScriptUnknown0x09(WiseScriptUnknown0x09 * data);
void freeWiseScriptUnknown0x0A(WiseScriptUnknown0x0A * data);
void freeWiseScriptUnknown0x0B(WiseScriptUnknown0x0B * data);
void freeWiseScriptUnknown0x0C(WiseScriptUnknown0x0C * data);
void freeWiseScriptUnknown0x11(WiseScriptUnknown0x11 * data);
void freeWiseScriptUnknown0x12(WiseScriptUnknown0x12 * data);
void freeWiseScriptUnknown0x14(WiseScriptUnknown0x14 * data);
void freeWiseScriptUnknown0x15(WiseScriptUnknown0x15 * data);
void freeWiseScriptUnknown0x16(WiseScriptUnknown0x16 * data);
void freeWiseScriptUnknown0x17(WiseScriptUnknown0x17 * data);
void freeWiseScriptUnknown0x1C(WiseScriptUnknown0x1C * data);
void freeWiseScriptUnknown0x23(WiseScriptUnknown0x23 * data);


// For debugging
void printWiseScriptHeader(WiseScriptHeader * header);
void printWiseScriptTexts(WiseScriptTexts * texts);
void printWiseScriptFileHeader(WiseScriptFileHeader * data);
void printWiseScriptUnknown0x03(WiseScriptUnknown0x03 * data);
void printWiseScriptUnknown0x04(WiseScriptUnknown0x04 * data);
void printWiseScriptUnknown0x05(WiseScriptUnknown0x05 * data);
void printWiseScriptUnknown0x06(WiseScriptUnknown0x06 * data);
void printWiseScriptUnknown0x07(WiseScriptUnknown0x07 * data);
void printWiseScriptUnknown0x08(WiseScriptUnknown0x08 * data);
void printWiseScriptUnknown0x09(WiseScriptUnknown0x09 * data);
void printWiseScriptUnknown0x0A(WiseScriptUnknown0x0A * data);
void printWiseScriptUnknown0x0B(WiseScriptUnknown0x0B * data);
void printWiseScriptUnknown0x0C(WiseScriptUnknown0x0C * data);
void printWiseScriptUnknown0x11(WiseScriptUnknown0x11 * data);
void printWiseScriptUnknown0x12(WiseScriptUnknown0x12 * data);
void printWiseScriptUnknown0x14(WiseScriptUnknown0x14 * data);
void printWiseScriptUnknown0x15(WiseScriptUnknown0x15 * data);
void printWiseScriptUnknown0x16(WiseScriptUnknown0x16 * data);
void printWiseScriptUnknown0x17(WiseScriptUnknown0x17 * data);
void printWiseScriptUnknown0x1C(WiseScriptUnknown0x1C * data);
void printWiseScriptUnknown0x1E(WiseScriptUnknown0x1E * data);
void printWiseScriptUnknown0x23(WiseScriptUnknown0x23 * data);


typedef struct {
  void (*cb_header)(WiseScriptHeader*);
  void (*cb_texts)(WiseScriptTexts*);
  void (*cb_0x00)(WiseScriptFileHeader*);
  void (*cb_0x03)(WiseScriptUnknown0x03*);
  void (*cb_0x04)(WiseScriptUnknown0x04*);
  void (*cb_0x05)(WiseScriptUnknown0x05*);
  void (*cb_0x06)(WiseScriptUnknown0x06*);
  void (*cb_0x07)(WiseScriptUnknown0x07*);
  void (*cb_0x08)(WiseScriptUnknown0x08*);
  void (*cb_0x09)(WiseScriptUnknown0x09*);
  void (*cb_0x0A)(WiseScriptUnknown0x0A*);
  void (*cb_0x0B)(WiseScriptUnknown0x0B*);
  void (*cb_0x0C)(WiseScriptUnknown0x0C*);
  void (*cb_0x0F)(void);                   // start form data?
  void (*cb_0x10)(void);                   // end form data?
  void (*cb_0x11)(WiseScriptUnknown0x11*);
  void (*cb_0x12)(WiseScriptUnknown0x12*);
  void (*cb_0x14)(WiseScriptUnknown0x14*);
  void (*cb_0x15)(WiseScriptUnknown0x15*);
  void (*cb_0x16)(WiseScriptUnknown0x16*);
  void (*cb_0x17)(WiseScriptUnknown0x17*);
  void (*cb_0x1C)(WiseScriptUnknown0x1C*);
  void (*cb_0x1E)(WiseScriptUnknown0x1E*);
  void (*cb_0x23)(WiseScriptUnknown0x23*);
} WiseScriptCallbacks;


typedef struct {
  size_t totalInflatedSize;
  size_t inflatedSize0x00;
  size_t inflatedSize0x06;
  size_t inflatedSize0x14;
  // Deflated files described in the WiseScript have a offset to the deflated
  // data, but this offset also has a offset which we can be found by iterating
  // through all file structs and note the largest end-deflate offset, use that
  // to subtract from the filesize.
  //
  // PE_filesize - largestEndDeflate
  //
  // This offset needs to be added to the offset described in the WiseScript to
  // get to the real offset of the deflated data.
  uint32_t inflateStartOffset;
} WiseScriptParsedInfo;


void initWiseScriptCallbacks(WiseScriptCallbacks * callbacks);
REWError parseWiseScript(const char * filepath, WiseScriptCallbacks * callbacks);
void stopWiseScriptParse(void);

WiseScriptParsedInfo * wiseScriptGetParsedInfo(const char * filepath);

// Debug print the parsed WiseScript structures
REWError wiseScriptDebugPrint(const char * filepath);

char * wiseScriptParsePath(char * path);

#endif
